import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';
import { ClientSession, SessionService } from '../../../../service/session.service';
import { Host } from '../../../../service/config.service';
import { NotifyService } from '../../../../service/notify.service';
import { DragService } from '../drag.service';
import { formatTime, getRealPath } from '../../../../api/util';

@Component({
  selector: 'app-filelist',
  templateUrl: './filelist.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['./filelist.component.css']
})
export class FilelistComponent implements OnInit, AfterViewInit, OnDestroy {

  @Input() listType: string;
  @Input() host: Host;
  @Input() path: string;
  @Input() partner: FilelistComponent;
  @Output() pathChange = new EventEmitter<any>();
  @Output() stateChange = new EventEmitter<any>();
  @Output() fileOper = new EventEmitter<any>();

  isSpinning: boolean = false;
  lastPath: string;
  files: any[];
  allChecked = false;
  indeterminate = false;
  tableSize: any = { x: '100px', y: '100px' };
  nameWidth = '300px';
  session: ClientSession;

  constructor(private eleRef: ElementRef, private notify: NotifyService, private sessionService: SessionService,
    private nzContexMenuService: NzContextMenuService,
    private message: NzMessageService,
    private dragSvc: DragService,
    private modal: NzModalService,
    public cd: ChangeDetectorRef) { }
  contextMenu(event: MouseEvent, menu: NzDropdownMenuComponent, index): void {
    event.stopPropagation();
    event.preventDefault();
    if (this.editIndex == index) return;
    this.selectedIndex = index;
    this.nzContexMenuService.create(event, menu);
  }

  //@ViewChild('nzTable') nzTableComponent: any;//NzTableComponent<any>;
  @ViewChild('nzTable') nzTableComponent: any;//NzTableComponent<any>;
  setTableSize() {
    let el_table_head = this.nzTableComponent.elementRef.nativeElement.querySelector('.ant-table-header');
    let el_filelist_head = this.nzTableComponent.elementRef.nativeElement.parentNode.querySelector('.sftp-filelist-title');
    let tabWidth = this.nzTableComponent.elementRef.nativeElement.parentNode.clientWidth - 4 - 10;//- padding - scoll-bar-width
    //- filelist-header(32) - table-header(43)
    let tabHeight = this.nzTableComponent.elementRef.nativeElement.parentNode.clientHeight - el_filelist_head.clientHeight - el_table_head.clientHeight;
    let nameWidth = tabWidth - 30 - 30 - 100 - 160;
    if (nameWidth < 100) nameWidth = 100;
    this.nameWidth = nameWidth + 'px';
    this.tableSize = {
      x: (tabWidth) + 'px',
      y: (tabHeight) + 'px'
    };
    let el_table_body = this.nzTableComponent.elementRef.nativeElement.getElementsByClassName('ant-table-body');
    if (el_table_body && el_table_body.length > 0) {
      el_table_body[0].style.height = tabHeight + 'px';
    }
  }
  fileSync = false;
  remoteSyncPath: string;
  @ViewChild('tplFileSync') tplFileSync: TemplateRef<any>;
  fileSyncChange(flag) {
    this.fileSync = flag;
    if (this.listType == 'Local') {
      if (flag) {
        this.remoteSyncPath = this.partner.path;
        this.modal.create({
          nzTitle: 'Auto File Sync',
          nzContent: this.tplFileSync,
          nzOkText: 'OK',
          nzOkType: 'primary',
          nzOnOk: () => {
            this.session.fileSync(this.path);
          },
          nzOnCancel: () => {
            this.fileSync = false;
            this.cd.markForCheck();
          },
          nzCancelText: 'Cancel'
        })
      }
      else {
        this.session.fileSync('');
      }
    }
  }
  changePath(path) {
    this.path = path;
    this.pathChange.emit(this.path);
    this.fileSyncChange(false);
  }
  runSession(cb) {
    if (this.session) {
      cb.call(this);
      return;
    }

    this.stateChange.emit('CONNECTING');
    let sessionType = this.listType == 'Local' ? 'local-sftp' : 'sftp';
    this.isSpinning = true;
    this.session = this.sessionService.newSession(sessionType, this.host);
    this.session.opened$.subscribe((path: any) => {
      this.fileSync = false;
      if (path && !this.path) {
        this.changePath(path);
      }
      this.stateChange.emit('CONNECTED');
      cb.call(this);
    })
    this.session.error$.subscribe(error => {
      this.stateChange.emit('ERROR');
      this.session.kill();
      this.session = null;
      this.isSpinning = false;
      this.message.create('error', error);
    })
    this.session.filechange$.subscribe((data: any) => {
      console.log(data.event, data.file);
      if (data.event == 'change' || data.event == 'add') {
        let file = data.file.replace(this.path, '');
        console.log(this.path, this.remoteSyncPath, file);
        this.partner.session.upload(this.path, this.remoteSyncPath, [{ type: '-', name: file }]);
      }
    })
    //this.session.start();
  }
  refreshList() {
    this.editIndex = -1;
    this.isSpinning = true;
    this.session.list(this.path).then((files: any) => {
      files.forEach(file => {
        file.mtime = formatTime(file.mtime)
      });
      this.files = files;
      this.isSpinning = false;
      this.lastPath = this.path;
      this.cd.markForCheck();
    }).catch((error) => {
      this.message.create('error', error);
    })
  }
  ngOnInit(): void {
    this.runSession(this.refreshList);
  }
  @ViewChild('nzTable', { read: ElementRef }) nzTable: ElementRef;
  @ViewChild('operMenuNewFile') operMenuNewFile: NzDropdownMenuComponent;
  ngAfterViewInit() {
    setTimeout(() => this.setTableSize());
    if (this.nzTable) {
      let elTableBody = this.nzTable.nativeElement.querySelector(".ant-table-body");
      elTableBody.addEventListener("mouseenter", ev => this.mouseenter(ev, null));
      elTableBody.addEventListener("mousemove", ev => this.mouseenter(ev, null));
      elTableBody.addEventListener("mouseout", ev => this.mouseout(ev, null));
      elTableBody.addEventListener("contextmenu", ev => this.nzContexMenuService.create(ev, this.operMenuNewFile));
    }
  }

  onRefresh() {
    this.runSession(this.refreshList);
  }
  onUpdir() {
    this.editIndex = -1;
    this.changePath(getRealPath(this.lastPath, '..', this.listType));
    this.onRefresh();
  }
  /*
  onFileBatchOper(type) {
    let fileList = [];
    this.files.forEach((file) => {
      if (file.checked) {
        fileList.push({ type: file.type, name: file.name });
      }
    })
    this.editIndex = -1;
    if (fileList.length == 0) {
      this.message.create('error', 'No File selected!');
      return;
    }
    this.fileOper.emit({ type: type, fileList: fileList });
  }*/
  inputChanged(e) {
    var evt = window.event || e;
    if (evt.keyCode == 13) {
      this.changePath(this.path);
      this.onRefresh();
    }
  }
  typeConvert(type) {
    switch (type) {
      case 'd': return 2;
      case 'l': return 1;
      case '-': return 0;
      default: return -1;
    }
  }
  sortOrder = {
    name: null,
    size: null,
    time: null
  }
  sortOrderChange(key) {
    for (var _key in this.sortOrder) {
      if (key != _key) this.sortOrder[_key] = null;
    }
  }
  typeSortFn = (a: any, b: any) => this.typeConvert(a.type) - this.typeConvert(b.type);
  nameSortFn = (a: any, b: any) => a.name.localeCompare(b.name);
  sizeSortFn = (a: any, b: any) => a.size - b.size;
  timeSortFn = (a: any, b: any) => a.mtime.localeCompare(b.mtime);

  checkCheckBoxChanged(): void {
    const allUnChecked = this.files.every(file => file.checked === false);
    this.allChecked = this.files.every(file => file.checked === true);
    this.indeterminate = !allUnChecked && !this.allChecked;
  }
  checkAll(value: boolean): void {
    this.files.forEach(file => {
      file.checked = value;
    })
    this.checkCheckBoxChanged();
  }
  checkOne(file, checked: boolean) {
    file.checked = checked;
    this.checkCheckBoxChanged();
  }
  getSelectedFileList(file) {
    let fileList = [];
    if (file.checked) {
      this.files.forEach((f) => {
        if (f.checked) {
          fileList.push({ type: f.type, name: f.name });
        }
      })
    } else {
      fileList.push({ type: file.type, name: file.name });
    }
    return fileList;
  }
  @ViewChild('tplNewFile') tplNewFile: TemplateRef<any>;
  addFolder() {
    let _addFileOrFolder = (type, title) => {
      this.newFileName = '';
      this.modal.create({
        nzTitle: title,
        nzContent: this.tplNewFile,
        nzOkText: 'OK',
        nzOkType: 'primary',
        nzOnOk: () => {
          this.runSession(() => {
            this.session.mkdir(this.newFileName)
              .then(() => this.refreshList())
              .catch((error) => {
                this.refreshList();
                this.message.create('error', error);
              });
          });
        },
        nzCancelText: 'Cancel'
      })
    }
    _addFileOrFolder('addFolder', 'New Directory');
  }
  /* rename */
  private oldFile: any;
  private newFileName: string;
  private editIndex: number;
  rename(file, index) {
    this.oldFile = file;
    this.editIndex = index;
    setTimeout(() => {
      let elFileNameInput = this.eleRef.nativeElement.querySelector(".filenameInput");
      if (elFileNameInput) {
        elFileNameInput.focus();
      }
    })
  }
  delete(file, index) {
    this.modal.confirm({
      nzTitle: 'Are you sure to delete?',
      nzContent: '',
      nzOkText: 'Yes',
      nzOkType: 'primary',
      nzOkDanger: true,
      nzOnOk: () => {
        this.runSession(() => {
          this.session.delete(this.path, this.getSelectedFileList(file))
            .then(() => this.refreshList())
            .catch((error) => {
              this.refreshList();
              this.message.create('error', error);
            });
        });
      },
      nzCancelText: 'No'
    })
  }
  updown(file) {
    this.fileOper.emit(this.getSelectedFileList(file));
  }
  isEditable(index) {
    return index === this.editIndex;
  }
  filenameChanged(name) {
    this.newFileName = name;
  }
  onInputKeyDown(e) {
    var evt = window.event || e;
    if (evt.keyCode == 13) { //Enter
      for (let i = 0; i < this.files.length; i++) {
        if (this.files[i] != this.oldFile && this.files[i].name == this.newFileName) {
          this.message.create('error', 'File Name duplicated!');
          return;
        }
      }
      if (this.newFileName == '') {
        this.message.create('error', 'File name is empty!');
        return;
      }
      let oldFileName = this.oldFile.name;
      this.oldFile.name = this.newFileName;
      this.runSession(() => {
        this.session.rename(oldFileName, this.newFileName)
          .then(() => this.refreshList())
          .catch((error) => {
            this.refreshList();
            this.message.create('error', error);
          });
      })
    } else if (evt.keyCode == 27) { //ESC
      this.editIndex = -1;
    }
  }
  onInputBlur() {
    this.editIndex = -1;
  }
  selectedIndex = -1;
  click(index) {
    this.selectedIndex = index;
    if (index != this.editIndex) this.editIndex = -1;
  }
  dblClick(file, index) {
    clearTimeout(this.dragSvc.timeOut);
    if (file.type == 'd' && this.editIndex != index) {
      this.editIndex = -1;
      this.selectedIndex = -1;
      this.allChecked = false;
      this.indeterminate = false;
      //this.pathChange.emit({ type: 'relative', path: file.name });
      this.changePath(getRealPath(this.lastPath, file.name, this.listType));
      this.onRefresh();
    }
  }
  mousedown(event: MouseEvent, item, index) {
    if (this.editIndex == index) return;
    this.click(index);
    clearTimeout(this.dragSvc.timeOut);
    this.dragSvc.timeOut = setTimeout(() => {
      this.dragSvc.dragStart(event, this.listType, this.session.sessionid, this.getSelectedFileList(item));
    }, 500);
  }
  mouseenter(event: MouseEvent, item) {
    if (item && item.type != 'd') return;
    this.dragSvc.setDragEndObj(this.listType, event.currentTarget, item ? item.name : null);
  }
  mouseout(event: MouseEvent, item) {
    if (item && item.type != 'd') return;
    this.dragSvc.setDragEndObj(this.listType, null, null);
  }
  ngOnDestroy(): void {
    if (this.session) this.session.kill();
  }
}
